package jehc.cloud.sentinel.init;

import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientAssignConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.util.AppNameUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import jehc.cloud.sentinel.Constants;
import jehc.cloud.sentinel.util.FlowRuleUtil;
import jehc.cloud.sentinel.util.SpringApplicationUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Properties;

/**
 * 注册管理限流器
 */
public class FlowRuleInitFunc implements InitFunc {
    private static final String APP_NAME = AppNameUtil.getAppName();//服务应用名称
    /**
     * nacos properties（配置中心自身属性 如可以指定访问Nacos下的Name pace 否则只能访问Nacos public空间下的文件配置）
     */
    private Properties properties;

    private FlowRuleUtil flowRuleUtil;

    @Override
    public void init() {
        flowRuleUtil = SpringApplicationUtils.getBean(FlowRuleUtil.class);
        initProperties();
        registerFlowRule();
        registerParamRule();
        if(!StringUtils.isBlank(flowRuleUtil.getTokenClientConfig())){
            registerClientConfigProperty();//注册客户端Netty通信超时
        }
        registerClientServerAssignProperty();
        initStateProperty();
    }

    /**
     * 初始化 Nacos自身属性配置 目的为接下面方法远程访问该属性下文件
     */
    private void initProperties() {
        this.properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, flowRuleUtil.getNacosAddress());
        properties.put(PropertyKeyConst.NAMESPACE, flowRuleUtil.getNacosNamespace());
        properties.put("username", flowRuleUtil.getUsername());
        properties.put("password", flowRuleUtil.getPassword());
    }

    /**
     * 管理普通限流
     */
    private void registerFlowRule() {
        ReadableDataSource<String, List<FlowRule>> ruleSource = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(),
                APP_NAME + Constants.FLOW_RULES_POSTFIX, new Converter<String, List<FlowRule>>() {
            @Override
            public List<FlowRule> convert(String source) {
                return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                });
            }
        });
        FlowRuleManager.register2Property(ruleSource.getProperty());
    }


    /**
     * 管理热点参数限流
     */
    private void registerParamRule() {
        ReadableDataSource<String, List<ParamFlowRule>> paramRuleSource = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(), APP_NAME + Constants.PARAM_RULES_POSTFIX, new Converter<String, List<ParamFlowRule>>() {
            @Override
            public List<ParamFlowRule> convert(String source) {
                return JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {
                });
            }
        });
        ParamFlowRuleManager.register2Property(paramRuleSource.getProperty());
    }


    /**
     * token server地址与端口
     * Nacos Data-ID为client_server token-sever服务端的地址 用于客户端 这样配置后 无需再客户端页面中集群流控中手动选择地址了
     * 当token server的IP和端口变更时，token client需要重新连接到新的server地址。
     */
    private void registerClientServerAssignProperty() {
        ReadableDataSource<String, ClusterClientAssignConfig> clientAssignDs = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(),
                flowRuleUtil.getServerAddress(), new Converter<String, ClusterClientAssignConfig>() {
            @Override
            public ClusterClientAssignConfig convert(String source) {
                return JSON.parseObject(source, new TypeReference<ClusterClientAssignConfig>() {
                });
            }
        });
        ClusterClientConfigManager.registerServerAssignProperty(clientAssignDs.getProperty());
    }



    /**
     * 设置应用状态,表示客户端
     */
    private void initStateProperty() {
        ClusterStateManager.applyState(ClusterStateManager.CLUSTER_CLIENT);
    }



    /**
     * 客户端配置
     */
    private void registerClientConfigProperty() {
        ReadableDataSource<String, ClusterClientConfig> clientConfigDs = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(),
                flowRuleUtil.getTokenClientConfig(), new Converter<String, ClusterClientConfig>() {
            @Override
            public ClusterClientConfig convert(String source) {
                return JSON.parseObject(source, new TypeReference<ClusterClientConfig>() {
                });
            }
        });
        ClusterClientConfigManager.registerClientConfigProperty(clientConfigDs.getProperty());
    }
}